package galaxies.starRegions;

import galaxies.GalaxySpiralBuilder2;

import java.util.ArrayList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme3.math.Vector2f;

import controller.AbstractController;

/*
 * Supplies inputs to create arm, core regions of spiral galaxies
 */
public class StarRegionSpiralBuilder {
	
	private static final Logger logger = Logger.getLogger(StarRegionSpiralBuilder.class.getName());
	
	public double coreWidth, coreSide, triangleSide;
	
	public ArrayList<StarRegionSpiralCoreModel> buildCoreRegions(int numArms, ArrayList<Integer> dims) {
		//------------------------------------------------------------------------
		//CORE
		/*
		 * Method:
		 * The core is an ellipsoid with its sides cut away to form flat planes.  Imagine taking a relatively flat, round cookie
		 * and cutting it into a triangle, square, or hexagon.  This is what the core looks like.  Arms are then attached
		 * to the flat sides.
		 * 
		 * To achieve this, the core is broken up into a series of triangles, points are generated in a base (zero rotation)
		 * triangle, then they are rotated around to their appropriate position in space and collision checked.  A hexagon
		 * core (6 arms) is composed of six equilateral triangles.  A triangle core (3 arms) is composed of 3 obtuse triangles.
		 * A square core (4 arms or 2 arms) is composed of 4 right triangles.
		 */
		
		Random rand = new Random();
		//Get galactic radius
		int tileRadius = (int) Math.sqrt(dims.get(0)*dims.get(0) + dims.get(1)*dims.get(1));
		logger.log(Level.INFO, "Tile radius= " + tileRadius);
		//Interior sides of the triangle, not facing outward from core
		double sideScale = 0.3;//0.1+0.2*rand.nextDouble(); //10 - 30% of radius
		triangleSide = sideScale*tileRadius; //Math.sqrt(tilesPerStar*coreStars/Math.PI);
		
		double thetaStep;
		int numSteps;
		//double theta;
		
		if (numArms == 2)
			numSteps = 4; //2 arms galaxies still have square cores
		else
			numSteps = numArms;
		
		thetaStep = 2*Math.PI/numSteps;
		
		//distance from origin to center of any flat outer wall of the core
		coreWidth = triangleSide*Math.cos(thetaStep/2);
		//length of any flat outer wall of the core
		coreSide = 2*triangleSide*Math.sin(thetaStep/2);		
		
		StarRegionSpiralCoreModelBuilder coreRegionFact = new StarRegionSpiralCoreModelBuilder();
				
		//ArrayList<MCoreStarRegion> theseRegions = new ArrayList<MCoreStarRegion>();
		//theseRegions.addAll(coreRegionFact.createCoreStarRegions(triangleSide, numSteps));		
		
		ArrayList<StarRegionSpiralCoreModel> coreRegions = coreRegionFact.createCoreStarRegions(triangleSide, numSteps);
		//END CORE
		//------------------------------------------------------------------------
		return coreRegions;
	}
	
	public ArrayList<StarRegionSpiralArmModel> buildArmRegions(int numArms, ArrayList<Integer> dims) {
		//------------------------------------------------------------------------
		//ARMS
		/*
		 * Arms are extended ellipses, composed of multiple segments and branches.  
		 * Every ellipse has long and short dimensions.
		 */

		//Calculate full-arm attributes
		//--------------------------------------------------
		//Same as with core.  Overestimate, then remove stars later.
		//int starsPerArm = (int) Math.ceil((double)armStars/(double)numArms);
		//extraStars += starsPerArm*numArms - armStars;
		
		int tileRadius = (int) Math.sqrt(dims.get(0)*dims.get(0) + dims.get(1)*dims.get(1));

		double maxArmLength = tileRadius-coreWidth;

		double shortDim = 0.0, longDim = 0.0;		
		shortDim = coreSide/2;
		longDim = 0.95*maxArmLength; //arm extends for 95% of maximum possible length
		//--------------------------------------------------

		//Create individual arm star regions
		//--------------------------------------------------
		StarRegionSpiralArmModelBuilder armStarRegionFact = new StarRegionSpiralArmModelBuilder();
		ArrayList<StarRegionSpiralArmModel> theseRegions = new ArrayList<StarRegionSpiralArmModel>();			

		double step = 2*Math.PI/numArms;
		double correction = step/2;
		if (numArms == 2) {
			correction -= correction/2;
		}

		double currRotation = 0;
		
		ArrayList<StarRegionSpiralArmModel> armRegions = new ArrayList<StarRegionSpiralArmModel>();

		//generates a list of MSectors, MArmStarRegions with number of stars in each already set
		for (int i = 0; i < numArms; i++) {
			//Generate armStarRegions
			//System.out.println("Generate regions for ARM#" + i);
			theseRegions.addAll(armStarRegionFact.createArmStarRegions(longDim, shortDim, numArms));
			armRegions.addAll(theseRegions);

			//Rotate and translate sectors and regions into correct positions
			//---------------------------------------------			
			//System.out.println("No. Regions= " + theseRegions.size());
			//System.out.println("");			
			for (int j = 0; j < theseRegions.size(); j++) {				
				StarRegionSpiralArmModel thisRegion = theseRegions.get(j);

				//System.out.println("No. Stars in region: " + thisRegion.getNumStars());

				//calculate appropriate arm rotation around core
				currRotation = step*(i+1) - correction;		
				//System.out.println("CurrRotation " + currRotation);
				thisRegion.setRotation(currRotation); //rotation around the region's base

				//Determine region's distance from origin, angle around origin (branches will be above or below the x-axis, and to the right of the y-axis)
				Vector2f currLocation = thisRegion.getLocation();				
				double magnitude = Math.sqrt(currLocation.x*currLocation.x + currLocation.y*currLocation.y);
				//initial angle that thisRegion has w.r.t. the origin.
				double angle;
				if (magnitude == 0) {
					angle = 0.0;
				}
				else {
					angle = Math.asin(currLocation.y/magnitude);
				}

				//System.out.println("Magnitude= " + magnitude);
				//System.out.println("Angle= " + angle);

				//Calculate translation to move region into place around core				
				//Translation from (0,0) necessary to align with core geometry
				float xCoreTrans = (float) (coreWidth*Math.cos(currRotation));
				float yCoreTrans = (float) (coreWidth*Math.sin(currRotation));
				//Translation from (x,y) to move to correct location in space around geometry
				float xPointTrans = (float) (magnitude*Math.cos(currRotation+angle));
				float yPointTrans = (float) (magnitude*Math.sin(currRotation+angle));				

				//further translation if the region has a rotationOffset
				double offsetRotation = thisRegion.getOffsetRotation();
				double fullRotation = thisRegion.getRotation();
				double b = thisRegion.getShortDim();
				double c = b*Math.sqrt(2);
				Vector2f offsetTranslation = new Vector2f().zero();
				if (offsetRotation > 0) {
					double phi = fullRotation - Math.PI/4;
					offsetTranslation.x = (float) (c*Math.cos(phi));
					offsetTranslation.y = (float) (c*Math.sin(phi));
				}
				else if (offsetRotation < 0) {
					double phi = fullRotation + Math.PI/4;
					offsetTranslation.x = (float) (c*Math.cos(phi));
					offsetTranslation.y = (float) (c*Math.sin(phi));
				}
				//System.out.println("OffRot= " + offsetRotation);
				//System.out.println("OffX= " + offsetTranslation.x);
				//System.out.println("OffY= " + offsetTranslation.y);
				thisRegion.setOffsetTranslation(offsetTranslation);
				
				//System.out.println("Region currLocation= " + thisRegion.getLocation().x + " " + thisRegion.getLocation().y);
				Vector2f translation = new Vector2f(xCoreTrans+xPointTrans,yCoreTrans+yPointTrans);
				//translation.add(offsetTranslation);//
				//System.out.println("New Location= " + translation.x + " " + translation.y);

				//adjust locations
				thisRegion.setLocation(translation); //note:  Do I need to add this rather than just set it?
			}	
			theseRegions.clear();	
			//---------------------------------------------

			//System.out.println("");
		}
		
		//END ARMS
		//------------------------------------------------------------------------
		return armRegions;
	}
	
	

}
